package com.neusoft.bizcore.auth.service.impl;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;

import com.neusoft.bizcore.auth.common.bean.UserBean;
import com.neusoft.bizcore.auth.converter.OAuth2ConfigConverter;
import com.neusoft.bizcore.auth.dto.OAuth2ConfigDTO;
import com.neusoft.bizcore.auth.model.OAuth2Config;
import com.neusoft.bizcore.auth.model.OAuth2User;
import com.neusoft.bizcore.auth.repository.OAuth2ConfigRepository;
import com.neusoft.bizcore.auth.repository.OAuth2UserRepository;
import com.neusoft.bizcore.auth.service.AuthUserService;
import com.neusoft.bizcore.auth.service.OAuth2Service;
import com.neusoft.bizcore.web.exception.ConfigErrorException;
import com.neusoft.bizcore.web.exception.UnitedException;
import com.neusoft.bizcore.web.utils.JsonUtils;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class OAuth2ServiceImpl implements OAuth2Service {
    @Autowired
    private OAuth2ConfigRepository oAuth2ConfigRepository;
    @Autowired
    private OAuth2ConfigConverter oAuth2ConfigConverter;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private OAuth2UserRepository oAuth2UserRepository;
    @Autowired
    private AuthUserService authUserService;

    @Override
    public OAuth2ConfigDTO getConfig() {
        final List<OAuth2Config> list = this.oAuth2ConfigRepository.findAll();
        if (list.size() > 0) {
            return this.oAuth2ConfigConverter.toDTO(list.get(0));
        }
        return null;
    }

    @Override
    public OAuth2ConfigDTO setConfig(OAuth2ConfigDTO dto) {
        OAuth2Config model = this.oAuth2ConfigConverter.toModel(dto);
        if (model.isNew()) {
            final List<OAuth2Config> list = this.oAuth2ConfigRepository.findAll();
            if (list.size() > 0) {
                return this.oAuth2ConfigConverter.toDTO(list.get(0));
            }
        }
        model = this.oAuth2ConfigRepository.save(model);
        return this.oAuth2ConfigConverter.toDTO(model);
    }

    @Override
    public void delete(long id) {
        this.oAuth2ConfigRepository.deleteById(id);
    }

    @Override
    public String getAccessToken(String code) {
        final OAuth2ConfigDTO config = this.getConfig();
        if (null == config) {
            throw new ConfigErrorException("OAuth2 配置错误");
        }

        final String getAccessTokenUrl = config.getBaseUrl() + config.getAccessTokenUri();
        final Map<String, String> body = new HashMap<>();
        body.put("grant_type", "authorization_code");
        body.put("client_id", config.getClientId());
        body.put("client_secret", config.getSecretId());
        body.put("code", code);
        body.put("redirect_uri", config.getRedirectUrl());
        final HttpEntity<Map<String, String>> entity = new HttpEntity<>(body);
        ResponseEntity<AccessToken> response;
        try {
            response =
                    this.restTemplate.exchange(getAccessTokenUrl, HttpMethod.POST, entity, AccessToken.class);
        } catch (final HttpClientErrorException e) {
            if (HttpStatus.UNAUTHORIZED.equals(e.getStatusCode())) {
                throw new UnitedException("获取Access Token失败[" + code + "]");
            }
            throw e;
        }

        OAuth2ServiceImpl.log.info("{}", response.getBody());

        return response.getBody().getAccess_token();
    }

    @Override
    public UserBean loadUserByOAuth2Code(String code) {
        final OAuth2ConfigDTO config = this.getConfig();
        if (null == config) {
            throw new ConfigErrorException("OAuth2 配置错误");
        }
        final String accessToken = this.getAccessToken(code);

        final String getUserInfoUrl = config.getBaseUrl() + config.getUserInfoUri() + "?access_token=" + accessToken;

        final String jsonStr = this.restTemplate.getForObject(getUserInfoUrl, String.class);

        final Map<String, Object> result = JsonUtils.jsonToMap(jsonStr);
        final String oauth2UserId = result.containsKey("username") ? (String) result.get("username") : null;

        final OAuth2User oAuth2User = this.oAuth2UserRepository.findByOauth2UserId(oauth2UserId);
        if (null == oAuth2User) {
            throw new ConfigErrorException("未绑定系统用户");
        }
        final UserBean userBean = this.authUserService.loadUserByUsername(oAuth2User.getUser().getUsername());
        return userBean;

    }

    @Override
    public String getOAuth2UserIdBySystemUser(String username) {
        return null;
    }

    @Getter
    @Setter
    @ToString
    static class AccessToken {
        String access_token = "";
        String token_type = "";
        String refresh_token = "";
        String scope = "";
    }

}
